/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 定义三个常用的状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 使用class关键字定义一个类
class MyPromise {
	// 调用类的时候会有一个执行器立即执行
	constructor(executor) {
		// executor 会有两个参数 执行成功之后的回调函数 执行失败之后的回调函数
		// try判断是否有异常抛出如果有就执行reject 变更状态
		try {
			executor(this.resolve, this.reject)
		} catch(e) {
			this.reject(e)
		}
	}
	
	// 定义类的状态 默认等于 pending 等待
	status = PENDING
	// 成功之后的返回值
	value = undefined
	// 失败之后的原因
	reason = undefined
	// 成功之后的回调 数组存储多个回调
	successCallback = []
	// 失败之后的回调 数组存储多个回调
	failCallback =  []
	
	resolve = value => {
		// 状态只能变更一次
		if(this.status !== PENDING) return
		
		// 执行成功之后的回调
		// 成功之后改变状态为成功
		this.status = FULFILLED
		
		// 赋值成功后的返回值
		this.value = value
		// 判断是否为异步 successCallvack是否为undefined
		while (this.successCallback.length) this.successCallback.shift()()
	}
	
	reject = reason => {
		// 状态只能变更一次
		if(this.status !== PENDING) return
		
		// 执行失败之后的回调
		// 失败之后改变状态为失败
		this.status = REJECTED
		
		// 赋值失败后的返回值
		this.reason = reason
		// 判断是否为异步 successCallback是否为undefined
		while (this.failCallback.length) this.failCallback.shift()()
		// this.failCallback && this.failCallback(this.reason)
	}
	
	then (successCallback, failCallback) {
		// 判断
		successCallback = successCallback ? successCallback : value => value
		failCallback = failCallback ? failCallback : reason => {throw reason}
		// then方法返回的是一个promise对象
		let promise2 = new MyPromise((resolve, reject) => {
			// then方法 判断状态执行对应的回调函数
			if(this.status === FULFILLED) {
				// 获取上一次的返回值
				setTimeout(() => {
					try {
						// 异步调用否则获取不到promise2
						let x = successCallback(this.value)
						// 传给下一个promise对象的then方法
						// 判断返回值X是一个普通值还是一个promise对象
						// 如果是普通值就直接调用resolve
						// 如果是promise对象 查看promise对象的状态
						// 再根据promise对象的状态调用resolve还是reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			}else if(this.status === REJECTED) {
				setTimeout(() => {
					try {
						// 异步调用否则获取不到promise2
						let x = failCallback(this.reason)
						// 传给下一个promise对象的then方法
						// 判断返回值X是一个普通值还是一个promise对象
						// 如果是普通值就直接调用resolve
						// 如果是promise对象 查看promise对象的状态
						// 再根据promise对象的状态调用resolve还是reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				}, 0)
			}else {
				// 状态为pending时保存successCallback和failCallback
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							// 异步调用否则获取不到promise2
							let x = successCallback(this.value)
							// 传给下一个promise对象的then方法
							// 判断返回值X是一个普通值还是一个promise对象
							// 如果是普通值就直接调用resolve
							// 如果是promise对象 查看promise对象的状态
							// 再根据promise对象的状态调用resolve还是reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							// 异步调用否则获取不到promise2
							let x = failCallback(this.reason)
							// 传给下一个promise对象的then方法
							// 判断返回值X是一个普通值还是一个promise对象
							// 如果是普通值就直接调用resolve
							// 如果是promise对象 查看promise对象的状态
							// 再根据promise对象的状态调用resolve还是reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}
	
	finally (callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => {throw reason})
		})
	}
	
	catch (failCallback) {
		return this.then(undefined, failCallback)
	}
	
	static all (array) {
		let result = []
		return new MyPromise((resolve, reject) => {
			let arrLength = 0
			function addResult (key, value) {
				// 把运行结果存储到result里面
				result[key] = value
				arrLength++
				// 解决array参数数组中有异步调用的问题
				if(array.length === arrLength) {
					resolve(result)
				}
			}
			for (let i = 0; i < array.length; i++) {
				// 判断是否是promise对象
				let current = array[i]
				if (current instanceof MyPromise) {
					// 是
					current.then(value => addResult(i, value), reason => reject(reason))
				} else {
					// 否
					addResult(i, array[i])
				}
			}
		})
	}
	
	static resolve(value) {
		// 判断是否是promise对象
		if(value instanceof MyPromise) {
			// 是
			return value
		}else {
			// 否
			return new MyPromise((resolve, reject) => {
				resolve(value)
			})
		}
	}
}

function resolvePromise(promise2, x, resolve, reject) {
	if(promise2 === x) {
		// promise不能返回相同的promise对象
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}
	if(x instanceof MyPromise) {
		// 判断返回值x是否是promise对象 如果是就调用then方法
		x.then(value => resolve(value), reason => reject(reason))
	}else {
		// 如果不是就直接调用resolve
		resolve(x)
	}
}

module.exports = MyPromise